﻿using System;
using System.Collections.Generic;
using System.Text;

namespace WC
{
    namespace Pattern
    {
        ////////////////////////////////////////////////////////////////////////////////////////////
        //  Singleton
        ////////////////////////////////////////////////////////////////////////////////////////////
        using System.Reflection;
        
        // [Singleton] fully lazy instantiation
        /*public sealed class Singleton
        {
            private Singleton()
            {
            }

            public static Singleton Instance { get { return Nested.instance; } }

            private class Nested
            {
                // Explicit static constructor to tell C# compiler
                // not to mark type as beforefieldinit
                static Nested()
                {
                }

                internal static readonly Singleton instance = new Singleton();
            }
        }*/
        // [Singleton] fully lazy instantiation (using .NET 4's Lazy<T> type)
        /*public sealed class Singleton
        {
            private static readonly Lazy<Singleton> lazy =
                new Lazy<Singleton>(() => new Singleton());

            public static Singleton Instance { get { return lazy.Value; } }

            private Singleton()
            {
            }
        }*/
        // [Singleton] Double-Check Lock Pattern
        public static class Singleton<T> where T : class
        {
            private static volatile T _instance;
            private static object _lock = new object();

            static Singleton()
            {
            }

            public static T Instance
            {
                get
                {
                    if (_instance == null)
                        lock (_lock)
                        {
                            if (_instance == null)
                            {
                                ConstructorInfo constructor = null;

                                try
                                {
                                    constructor = typeof(T).GetConstructor(BindingFlags.Instance | BindingFlags.NonPublic, null, new Type[0], null);
                                }
                                catch (Exception exception)
                                {
                                    throw new SingletonException(exception);
                                }

                                if (constructor == null || constructor.IsAssembly)
                                    throw new SingletonException(string.Format("A private or protected constructor is missing for '{0}'.", typeof(T).Name));

                                _instance = (T)constructor.Invoke(null);
                            }
                        }

                    return _instance;
                }
            }
        }

        ////////////////////////////////////////////////////////////////////////////////////////////
        //  SingletonException
        ////////////////////////////////////////////////////////////////////////////////////////////
        public class SingletonException : Exception
        {
            public SingletonException(string s)
                : base(s)
            {
            }
            public SingletonException(Exception innerException)
                : base(null, innerException)
            {
            }
            public SingletonException(string message, Exception innerException)
                : base(message, innerException)
            {
            }
        }
    }
}
